from PySide6.QtCore import (QRect,QTimer)
from PySide6.QtGui import (QIcon,Qt,QCloseEvent,QPainter, QPen, QColor,QTextCursor)
from PySide6.QtWidgets import (QApplication,QGraphicsView, QGraphicsScene, QMainWindow,QWidget)

from sys import exit
from conf.GameLogin import *
# 引入ui配置文件
from conf.Edit.Ui_Form import *
from conf.Edit.MyLael import *
from conf.Edit.MapMenus import *
from map.mapManage.mapMgr import *
from conf.DiaLogResult import *
from conf.Edit.EditSource import *
from conf.GameManger import Edit_GC as EditGC
from conf.Edit.EditSkills import EditSkillsUI

from lib.run import *
from threading import Thread
from datetime import datetime

import subprocess

# 初始化窗体
class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.W = 1300
        self.H = 800
        self.ui = Ui_Form(self,self.W,self.H)#实例化UI对象
        self.setMaximumHeight(self.H)
        self.setMinimumHeight(self.H)
        self.setMaximumWidth(self.W)
        self.setMinimumWidth(self.W)

        self.setWindowTitle("魔塔编辑器 v0.1.0")
        self.setWindowIcon(QIcon("lib/images/title.png"))
        # 初始化加载楼层数据
        MapMgr.loadMap()
        self.__initConf()
        self.Render_Controls()
        self.resertPreView()

        # 初始化菜单
        self.MMenus = MapMenus(self)
        # 事件编辑器开窗
        self.EventManage:ChildUI = None
        # 素材编辑器
        self.editSource = EditSourceUI(self)
        # 技能编辑器
        self.editSkills = EditSkillsUI(self)
    
    def closeEvent(self, event: QCloseEvent) -> None:
        # 如果有未保存的变更
        if not self.IsSaveMap:
            if self.ui.ShowDiaLog_Window("有未保存的变更,是否保存后退出?") == DiaLogResult.OK:
                self.saveMap()
        exit(0)
    
    def __initConf(self):
        # 存放当前地图所有的资源
        self.MapLabel = []
        # 存放当前楼层的事件点
        self.MapEventPoint:list[MyLabel] = []
        """初始化配置"""
        # 加载地图素材配置
        self.SourceMgr = None
        # 加载已经注册的素材缓存
        self.SourceCache = {}
        # 是否已经启动了游戏窗口
        self.RunGame = False

        self.GC = EditGC()
        self.SourceMgr = self.GC.SourceMgr

        # 当前最大的资源key, 注册资源时就不用手动输入了,自动判定
        self.SourceMaxKey = len(self.SourceMgr)
        for s in self.SourceMgr:
            self.SourceCache[f"{self.SourceMgr[s]['type']}-{self.SourceMgr[s]['index']}"] = self.SourceMgr[s]
                
        # 当前鼠标是否选中了素材
        self.MouseCurrowItem = None

        self.FloorName = self.GC.GC_Scripts["MainFloor"]
        
        res = MapMgr.getByMapName(self.FloorName)
        if res.type == DiaLogResult.false:
            self.ui.ShowDiaLog_Window(f"加载地图失败,请检查全局脚本定义的楼层名称: [{self.FloorName}] 是否正确",2)
            exit(0)
            
        self.MapData = res.data
        self.map = self.MapData["map"]
        self.mapEvn = self.MapData["events"]
        # 加载素材区域 [素材名,素材帧]
        ofstX = 0
        for sour in self.GC.MapImages:
            ofstX += self.setScroll(sour["name"],ofstX,sour["anim"])

        # 加载地图 ,需要放在加载其他素材之前, 避免提前渲染地图之后,把选中框遮挡住
        self.render_map()
        # 选中框
        pix = QPixmap("lib/images/item_cursor.png")
        self.item_selected = MyLabel(self,500,0,35,35,"",pix=pix.copy(0,0,32,32))
        self.item_selected.pixCopy = pix.copy(32,0,32,32)
        self.item_selected.setVisible(False)
        self.item_selected_show = False  # 默认关闭选中框
        self.item_selected_index = False

        # 加载定时器
        self.qTime = QTimer()
        self.qTime.timeout.connect(self.update_win)
        self.qTime.start(300)

        self.IsSaveMap = True  # 是否允许保存地图-> (当前地图是否有变更,如果有 = False) , 
        # 是否触发了双击
        self.MouseDouble = False
        # 是否点击左键
        self.MousePress = False
        # 连续生成的路径点
        self.LastAddPointDict = []
        # 根据路径点渲染
        self.renderPoint = {}

    def Render_Controls(self):
        """窗口上生成组件"""
        # 按钮 -> 确认保存地图
        self.ui.createBtnBox("保存地图",15,460,event=self.saveMap)
        # 按钮 -> 全局脚本
        self.ui.createBtnBox("技能编辑器",15,490,event=lambda:self.editSkills.showUI())
        # 按钮 -> 启动游戏
        self.ui.createBtnBox("启动游戏",15,520,event=self.startGame)

        # 预览图
        self.ui.createLab("当前选中",320,520)
        self.selectPreView = MyLabel(self,400,520,50,50,"")
        # 按钮 -> 取消选中资源
        self.clearSelect = self.ui.createBtnBox("取消选中",250,520,event=self.resertPreView)

        #当前地图名:
        self.actualMapName = self.ui.createLab(f"当前地图:",180,460)
        # 生成所有的地图选项下拉框
        self.comboxMap = self.ui.createComboBox(240,460,array=self.GC.MapNameList,w=120)
        self.comboxMap.setCurrentText(self.FloorName)
        # 切换地图按钮
        self.changeMapBtn = self.ui.createBtnBox("切换地图",370,460,event=self.chagneMap)
        self.BtnDelMap = self.ui.createBtnBox("删除地图",435,460,event=self.delMap)
        
        self.ui.createLab(f"地图名称:",180,490)
        self.NewMapName = self.ui.createLineBox(251,490)
        self.NewMapBtn = self.ui.createBtnBox("新增地图",370,490,event=self.NewMap)

        # 渲染在出生点,表示勇士默认的坐标
        self.renderHeroPos:QLabel = self.ui.createLab(None,0,0,32,32,style="background-color:rgba(0,0,0,0)")
        self.renderHeroPos.setPixmap(QPixmap(f'lib/images/editIco.png').scaled(QSize(32,32)))
        self.renderHeroPos.move(int(self.GC.Hero['y'])*32,int(self.GC.Hero['x'])*32)
        self.renderHeroPos.setEnabled(True)

        # 显示日志
        self.EditLogList = self.ui.createPlainTextBox(10,580,450,200,form=self)
        self.EditLogList.setFocusPolicy(Qt.NoFocus)
        self.EditLogList.setMaximumBlockCount(200) # 设置最大行数超过指定行自动消失
        # self.EditLogList.updateRequest.connect(self.EditLoUpdateRequest)
        # self.EditLogList.update
        self.addLog("编辑器组件加载完成!",level=1)

    def keyReleaseEvent(self, e:QKeyEvent) -> None:
        """监听键盘抬起事件"""
        if e.key() == Qt.Key().Key_Escape:
            self.MMenus.hide()
            self.resertPreView()
        return super().keyReleaseEvent(e)


    # 刷新页面
    def update_win(self):
        # 渲染动画
        # 在连续生成图块的时候暂停播放动画
        # if len(self.LastAddPointDict):
        #     return
        for lab in self.MapLabel:
            lab.playLabAnim()
            
        if self.item_selected_index:
            self.item_selected.setPixmap(self.item_selected.pix)
        else:
            self.item_selected.setPixmap(self.item_selected.pixCopy)
        self.item_selected_index = not self.item_selected_index
        self.update()
    
    # 根据地图文件渲染
    def render_map(self):
        """根据地图文件渲染"""
        wallCache = {}
        for y in range(len(self.map)):
            for x in range(len(self.map[y])):
                data = self.SourceMgr.get(str(self.map[y][x]))
                if data:
                    wallCa = wallCache.get(data["type"])
                    if wallCa is None:
                        wallCa = QPixmap(f'lib/images/{data["type"]}.png')
                        wallCache[data["type"]] = wallCa
                    wall = wallCa
                    wallC = wall.copy(0,data["index"]*32,data["rect"][0],data["rect"][1])
                    lab = MyLabel(self,x*data["rect"][0],y*data["rect"][1], data["rect"][0],data["rect"][1],f"{y},{x}",wallC,wall,tag="map")
                    lab.data = data
                    lab.animMax = data["animMax"]
                    self.MapLabel.append(lab)
                else:
                    # 未知的地方就显示地板
                    wall = QPixmap(f'lib/images/terrains.png')
                    wallC = wall.copy(0,0,32,32)
                    lab = MyLabel(self,x*32,y*32, 32,32,f"{y},{x}",wallC,wall)
                    lab.data = data
                    lab.tag = "map"
                if not self.mapEvn.get(f"{y},{x}") is None:
                    # 创建一个Label来绘制事件点
                    point = self.ui.createLab(None,x*32+25,y*32+25,5,5)
                    point.setStyleSheet("background-color: #FF0000;")
                    point.show()
                    self.MapEventPoint.append(point)

    def updateMap(self):
        """更新地图"""
        index = 0
        # 更新数据 ,必须执行, 不然更换地图之后, 事件点还是渲染再页面, 哪怕地图文件已经没有了这点的事件
        for i in self.MapEventPoint:
            i.deleteLater()
            i.hide()
        self.MapEventPoint.clear()
        wallCache = {}

        for y in range(len(self.map)):
            for x in range(len(self.map[y])):
                data = self.SourceMgr.get(str(self.map[y][x]))
                if data:
                    wallCa = wallCache.get(data["type"])
                    if wallCa is None:
                        wallCa = QPixmap(f'lib/images/{data["type"]}.png')
                        wallCache[data["type"]] = wallCa
                    wall = wallCa
                    wallC = wall.copy(0,data["index"]*32,data["rect"][0],data["rect"][1])
                    lab:MyLabel = self.MapLabel[index]
                    lab.pix = wallC
                    lab.pixCopy = wall
                    lab.data = data
                    lab.tag = "map"
                    lab.animMax = data["animMax"]
                    lab.setPixmap(wallC)
                    lab.show() 
                else:
                    self.addLog(f"坐标点:[{y},{x}],出现未知的图块.")
                    # 未知的地方就显示地板
                    wall = QPixmap(f'lib/images/terrains.png')
                    wallC = wall.copy(0,0,32,32)
                    lab:MyLabel = self.MapLabel[index]
                    lab.data = data
                    lab.setPixmap(wallC)
                    
                if not self.mapEvn.get(f"{y},{x}") is None:
                    point = self.ui.createLab(None,x*32+25,y*32+25,5,5)
                    point.setStyleSheet("background-color: #FF0000;")
                    point.show()
                    self.MapEventPoint.append(point)
                index += 1

    def setScroll(self,imgName,offset,animFrame=0):
        """加载素材区域"""
        pix = QPixmap(f'lib/images/{imgName}').copy()
        lab = MyLabel(self,0,0, pix.width(),pix.height(),name=imgName.split(".")[0],pix=pix,offset=offset+500,tag="source")
        lab.animMax = animFrame
        # 获取素材的宽度
        imgWidth = pix.width() +32
        terrainsScroll = QScrollArea(self)
        terrainsScroll.setWidget(lab)
        terrainsScroll.setGeometry(QRect(500+offset,0, imgWidth,self.H))
        terrainsScroll.setObjectName(imgName)
        
        terrainsScroll.setStyleSheet("background-color: rgb(210, 210, 210);")
        lab.parent = terrainsScroll

        # 通过遍历添加素材区域,将当前生成的素材区宽返回
        return imgWidth

    def saveMap(self,force=False):
        """保存地图数据"""
        self.comboxMap.setCurrentText(self.FloorName)
        if self.IsSaveMap and not force:
            self.addLog(f"保存地图:{self.FloorName} 失败,没有任何变更","red")
            return
        
        self.MapData["map"] = self.map
        self.MapData["floorName"] = self.FloorName
        self.MapData["events"] = self.mapEvn
        
        res = MapMgr.SaveMap(self.MapData,self.FloorName)
        if res.type == DiaLogResult.false:
            self.addLog(res.data,"red")
            return
        
        self.addLog(f"保存地图:{self.FloorName} 成功","green")
        self.IsSaveMap = True

    def chagneMap(self,MapName=None):
        """切换地图"""
        # 如果有未保存的变更
        if not self.IsSaveMap:
            if self.ui.ShowDiaLog_Window("地图已修改,是否保存?",1) == DiaLogResult.OK:
                # 执行保存地图时,会将地图列表的下拉框选中内容修正为当前层(执行切换楼层之前的名称,
                # 所以这里需要保存一下需要切换的目标楼层名称,保存地图修改之后将下拉框的选项更新为目标楼层)
                tmpName = self.comboxMap.currentText()
                self.saveMap()
                self.comboxMap.setCurrentText(tmpName)
            else:
                res = MapMgr.resetMap(self.FloorName)
                if res.type == DiaLogResult.false:
                    self.addLog(res.data,"red")
                    return
        self.IsSaveMap = True  # 切换地图之后, 还不会对地图元素造成修改
        currName = MapName or self.comboxMap.currentText()
        # 判断下拉框内是否有地图名称以及是否就是当前楼层,
        if currName == "" or currName == self.FloorName:
            self.addLog(f"当前楼层不可切换","orange")
            return
        res = MapMgr.getByMapName(currName)
        if res.type != DiaLogResult.true:
            self.addLog(res.data,"orange")
            return
        newMap = res.data
        if newMap is None:
            self.addLog(f"无效地图","red")
            self.comboxMap.setCurrentText(self.FloorName)
            return
        
        self.FloorName = currName
        self.map = newMap["map"]
        self.mapEvn = newMap["events"]
        self.addLog(f"切换地图:{currName} 完成","green")
        self.updateMap()
        self.MapData = newMap
        # 关闭菜单
        self.MMenus.hide()
        # 切换楼层时隐藏掉出生点图标
        self.renderHeroPos.setVisible(False)
        self.comboxMap.setCurrentText(self.FloorName)
        # 判断切换的新楼层是否是主角出生的楼层,如果不是,那么不激活主角的出生点图标
        if currName == self.GC.GC_Scripts["MainFloor"]:
            self.renderHeroPos.setVisible(True)
        self.resertPreView()

    def NewMap(self):
        """新增地图"""
        MapName = self.NewMapName.text().replace(" ","")
        if MapName == "":
            self.ui.ShowDiaLog_Window("新地图名称不能为空",2)
            self.addLog(f"新地图名称不能为空","red")
            return
        if MapName in self.GC.MapNameList:
            return self.ui.ShowDiaLog_Window("该地图名称已被使用",2)
        res = MapMgr.NewMap(MapName)
        if res.type == DiaLogResult.true:
            # 将新添加的地面名称添加到全局脚本
            self.GC.GC_Scripts["FloorFiles"].append(MapName)
            self.GC.saveGCScript()
            self.comboxMap.addItem(MapName)
            self.addLog(f"新地图->{MapName} 创建完成")
            if self.ui.ShowDiaLog_Window("新地图创建成功,是否切换") == DiaLogResult.OK:
                self.comboxMap.setCurrentText(MapName) # 切换地图的同时,将当前地图名称切换
                self.chagneMap(MapName)
            return
        self.ui.ShowDiaLog_Window(res.data,2)

    def delMap(self):
        """删除地图"""
        # 从全局脚本 FloorFiles 中移除 该楼层的名称
        # 如果 删除的是初始楼层, 就是说 MainFloor ==  self.FloorName, 那么将 MainFloor 切换到地图列表的首位,
        # 如果地图列表为空,那么读取默认楼层模板加载,且 self.FloorName = "新楼层", MainFloor = self.FloorName
        # 被移除的地图不删除文件,防止误操作,但需要修改文件前缀名为 rm_
        # 从选择地图下拉框中去除

        # 只有剩余地图大于1 才可以删除, 以后再改
        if self.comboxMap.count() == 1:
            self.addLog(f"删除地图:{delName} 失败,至少需要保留一张地图")
            self.ui.ShowDiaLog_Window(f"当前只剩最后一张地图了,留着替换吧!")
            return
        delName = self.comboxMap.currentText()
        if self.ui.ShowDiaLog_Window(f"是否确认删除地图:[{delName}](被删除的地图会以 rm_{delName}.json.bak 的文件格式存储)") == DiaLogResult.Cancel:
            return
        self.comboxMap.removeItem(self.comboxMap.currentIndex())

        if delName == self.FloorName:
            # 切换到第一张地图
            self.chagneMap(self.comboxMap.itemText(0))
            # 更新初始楼层
            self.GC.GC_Scripts["MainFloor"] = self.comboxMap.itemText(0)
        
        self.GC.GC_Scripts["FloorFiles"].remove(delName)
        with open(f"conf/GlobalScript.json","w",encoding="utf8") as file:
            file.write(JsonSave(self.GC.GC_Scripts))
        # 改名
        os.rename("map/mapFile/"+delName+".json",f"map/mapFile/rm_{delName}.json.bak")
        self.addLog(f"删除地图:{delName} 完成,已更名为->rm_{delName}.json.bak")
        

    def resertPreView(self):
        """ 取消选中素材状态 """
        self.MouseCurrowItem = None
        # 清除预览图
        self.selectPreView.clear()
        self.MouseDouble = False  # 取消双击状态

    def startGame(self):
        if self.RunGame:
            self.ui.ShowDiaLog_Window("当前已经启动了一个游戏窗口")
            return
        self.RunGame = True
        threadGame = Thread(target=Run,args=(780,450,"魔塔(编辑状态).性能拉满",60,self),daemon=True)
        threadGame.start()

    def addLog(self,msg="空消息",color="black",level=-1):
        """追加日志信息,level ->1:普通,2:警告,3:错误"""
        if level == 1:
            color = "green"
        if level == 2:
            color = "orange"
        if level == 3:
            color = "red"
        nowTime = str(datetime.today()).split(" ")[1].split(".")[0]
        self.EditLogList.appendHtml(f"<span style=\"color:teal\">{nowTime}</span> ><span style=\"color:{color}\">{msg}</span>")
        self.EditLogList.moveCursor(QTextCursor.End)

    # def EditLoUpdateRequest(self,rec,dy):
        # """日志组件滚动时触发"""
        # print(dy)      #信号内传递有滚动的y像素值

if __name__ == "__main__":
    app = QApplication([])
    widget = MyWidget()
    widget.show()
    exit(app.exec())

""" 
1. 增加遮罩层(天空层), 可在遮罩层添加图块, 使用画布绘制

未完待续......
"""
